package club.itstudio.IContainer.controller.student;

import club.itstudio.IContainer.annotation.LoginRequired;
import club.itstudio.IContainer.annotation.StudentRequired;
import club.itstudio.IContainer.container.ContainerArgs;
import club.itstudio.IContainer.container.ContainerManager;
import club.itstudio.IContainer.entity.Container;
import club.itstudio.IContainer.entity.CourseExperiment;
import club.itstudio.IContainer.entity.User;
import club.itstudio.IContainer.exception.BaseException;
import club.itstudio.IContainer.exception.ParamInvalidException;
import club.itstudio.IContainer.exception.PermissionException;
import club.itstudio.IContainer.service.*;
import club.itstudio.IContainer.util.Random;
import club.itstudio.IContainer.util.Result;
import club.itstudio.IContainer.util.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;

@RestController
@RequestMapping("/student/container")
public class StudentContainerController {

    private StudentCourseService studentCourseService;
    private CourseExperimentService courseExperimentService;
    private ContainerService containerService;
    private ContainerManager containerManager;
    private AuthService authService;
    private ImageService imageService;
    private ServerPortService serverPortService;
    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Autowired
    public void setServerPortService(ServerPortService serverPortService) {
        this.serverPortService = serverPortService;
    }

    @Autowired
    public void setImageService(ImageService imageService) {
        this.imageService = imageService;
    }

    @Autowired
    public void setStudentCourseService(StudentCourseService studentCourseService) {
        this.studentCourseService = studentCourseService;
    }

    @Autowired
    public void setCourseExperimentService(CourseExperimentService courseExperimentService) {
        this.courseExperimentService = courseExperimentService;
    }

    @Autowired
    public void setContainerService(ContainerService containerService) {
        this.containerService = containerService;
    }

    @Autowired
    public void setContainerManager(ContainerManager containerManager) {
        this.containerManager = containerManager;
    }

    @Autowired
    public void setAuthService(AuthService authService) {
        this.authService = authService;
    }


    @PostMapping
    @LoginRequired
    @StudentRequired
    public Result startContainer(@RequestParam int experimentID, @RequestParam int courseID) throws IOException {
        User student = authService.threadLocalUsers.get();

        if (containerService.getByUserIDAndExperimentID(student.id, experimentID) != null) {
            throw new BaseException("已存在此实验容器，请勿重复启动");
        }

        if(studentCourseService.getByUserIDAndCourseID(student.id, courseID) == null)
            throw new ParamInvalidException("不存在该课程");

        CourseExperiment experiment = courseExperimentService.getByID(experimentID);
        if (experiment == null) throw new ParamInvalidException("不存在该实验");

        if (experiment.courseID != courseID) throw new ParamInvalidException("该实验不属于此课程");

        if (experiment.endTime < System.currentTimeMillis()) throw new BaseException("实验时间已截止");



        ContainerArgs containerArgs = new ContainerArgs();
        containerArgs.setName(String.valueOf(System.currentTimeMillis()));
        containerArgs.setMemoryLimit(experiment.memoryLimit + "m");
        containerArgs.setCpuShareLimit(String.valueOf(experiment.cpuShareLimit));
        containerArgs.setCpuSetLimit(String.valueOf(experiment.cpuSetLimit));
        containerArgs.setNewPassword(Random.getRandomStr(6));
        containerArgs.setNetworkName(experiment.networkName);
        containerArgs.setImageName(imageService.getByImageID(experiment.imageID).imageName);
        containerArgs.setSshPort(serverPortService.getRandomServerPort());

        if (containerArgs.getSshPort() == 0) throw new BaseException("当前容器数量过多，请稍后再试");
        containerManager.Create(containerArgs);

        Container container = new Container();
        container.containerName = containerArgs.getName();
        container.userID = student.id;
        container.experimentID = experimentID;
        container.createdTime = System.currentTimeMillis();
        container.endTime = System.currentTimeMillis() + (long) experiment.containerAliveTime * 60 * 60 * 1000;
        container.status = 1;
        container.sshPort = containerArgs.getSshPort();
        container.sshPassword = containerArgs.getNewPassword();
        containerService.insert(container);

        return ResultUtil.success();
    }

    @LoginRequired
    @StudentRequired
    @GetMapping
    public Result getContainers() {
        User user = authService.threadLocalUsers.get();
        String sql = "SELECT\n" +
                "\tcontainer.*,\n" +
                "\tcourse_experiment.experiment_name \n" +
                "FROM\n" +
                "\tcontainer\n" +
                "\tJOIN course_experiment ON container.experiment_id = course_experiment.id \n" +
                "WHERE\n" +
                "\tuser_id = ? \n" +
                "ORDER BY\n" +
                "\tid DESC";
        List<HashMap<String, Object>> data = jdbcTemplate.query(sql, (resultSet, i) -> {
            HashMap<String, Object> result = new HashMap<>();
            result.put("id", resultSet.getInt("id"));
            result.put("container_name", resultSet.getString("container_name"));
            result.put("created_time", resultSet.getLong("created_time"));
            result.put("end_time", resultSet.getLong("end_time"));
            result.put("status", resultSet.getInt("status"));
            result.put("ssh_port", resultSet.getInt("ssh_port"));
            result.put("ssh_password", resultSet.getString("ssh_password"));
            result.put("experiment_name", resultSet.getString("experiment_name"));
            return result;
        }, user.id);
        return ResultUtil.success(data);
    }


    @PostMapping("/stop")
    @LoginRequired
    @StudentRequired
    public Result StopContainer(@RequestParam int containerID) throws IOException {
        Container container = containerService.getByID(containerID);
        if (container == null) throw new ParamInvalidException();

        User student = authService.threadLocalUsers.get();
        if (container.userID != student.id) throw new PermissionException();

        if (container.status != 1) throw new BaseException("容器未运行");

        containerManager.Stop(container.containerName);
        container.status = 0;
        containerService.updateStatus(container);

        return ResultUtil.success();
    }


    @PostMapping("/remove")
    @LoginRequired
    @StudentRequired
    public Result RemoveContainer(@RequestParam int containerID) throws IOException {
        Container container = containerService.getByID(containerID);
        if (container == null) throw new ParamInvalidException();

        User student = authService.threadLocalUsers.get();
        if (container.userID != student.id) throw new PermissionException();

        if (container.status == 1) throw new BaseException("删除前请先关闭容器");
        if (container.status == 2) throw new BaseException("不要重复删除容器");

        containerManager.Remove(container.containerName);
        container.status = 2;
        containerService.updateStatus(container);

        return ResultUtil.success();
    }
}
